package cn.lcl.util;

import oracle.jdbc.OracleTypes;
import oracle.jdbc.oracore.OracleType;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.log4j.Logger;

import java.sql.*;
import java.util.List;

/**
 * Created by licho on 2017/8/15 0015.
 */
public class SessionImpl implements Session,Query {

    private static  final Logger log= Logger.getLogger(SessionImpl.class);

    private Connection conn;//代表当前连接

    private boolean isTransaction=false;//是否处于事务环境中

    private TransactionStatus staus=TransactionStatus.NO_ACTIVE;//当前事务状态

    private  QueryRunner runner=new QueryRunner();

    private boolean isClosed=false;
    public SessionImpl(Connection con){
        this.conn=con;
    }
    @Override
    public void beginTransaction() {
        if(staus.equals(staus.ACTIVE)){
            throw new RuntimeException("你已经开启事务,不能重复开启");
        }
        try {
            conn.setAutoCommit(false);//开启事务
            staus=TransactionStatus.ACTIVE;//开启事务
            log.info("事务已开启");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void commit() {
        if(!staus.equals(TransactionStatus.ACTIVE)){
            throw new RuntimeException("你还未开启事务");
        }
        try {
            conn.commit();//提交事务更改
            conn.setAutoCommit(true);//该次事务结束
            staus=TransactionStatus.COMMITED;
        } catch (SQLException e) {
            log.debug("事务提交失败",e);
        }
    }

    @Override
    public void rollback() {
        if(!staus.equals(TransactionStatus.ACTIVE)){
            throw new RuntimeException("未处在激活的事务中");
        }
        try{
            conn.rollback();
            staus=TransactionStatus.ROLLBACK;
        } catch (SQLException e) {
           log.debug("事务回滚失败");
        }
    }

    @Override
    public Query getQuery() {
        return  this;
    }


    @Override
    public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
        assertSessionNotClosed();
        return runner.query(conn,sql,rsh,params);
    }

    @Override
    public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {
        assertSessionNotClosed();
        return runner.query(conn,sql,rsh);
    }

    @Override
    public int update(String sql) throws SQLException {
        assertSessionNotClosed();
        return runner.update(conn,sql);
    }

    @Override
    public int update(String sql, Object param) throws SQLException {
        assertSessionNotClosed();
        return runner.update(conn,sql,param);
    }

    @Override
    public int update(String sql, Object... params) throws SQLException {
        assertSessionNotClosed();
        return runner.update(conn,sql,params);
    }

    @Override
    public <T> T insert(String sql, ResultSetHandler<T> rsh) throws SQLException {
        assertSessionNotClosed();
        return runner.insert(conn,sql,rsh);
    }

    @Override
    public <T> T insert(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
        assertSessionNotClosed();
        return runner.insert(conn,sql,rsh,params);
    }

    @Override
    public <T> T insertBatch(String sql, ResultSetHandler<T> rsh, Object[][] params) throws SQLException {
        assertSessionNotClosed();
        return runner.insertBatch(conn,sql,rsh,params);
    }

    @Override
    public int execute(String sql, Object... params) throws SQLException {
        assertSessionNotClosed();
        return runner.execute(conn,sql,params);
    }

    @Override
    public <T> List<T> execute(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
        assertSessionNotClosed();
        return runner.execute(conn,sql,rsh,params);
    }

    @Override
    public int[] batch(String sql, Object[][] params) throws SQLException {
        assertSessionNotClosed();
        return runner.batch(conn,sql,params);
    }

    public enum TransactionStatus{
        NO_ACTIVE,//未开启状态
        ACTIVE,//事务中
        COMMITED,//已经提交
        ROLLBACK;//已经回滚
    }

    @Override
    public void close() {
        try {
            if(conn==null)
                return;
            conn.close();
            this.conn=null;
            this.isClosed=true;
            log.info("连接已经返回给连接池");
            JdbcUtils.close();//移除当前线程的Session.这个地方实现有瑕疵，耦合了
        } catch (SQLException e) {
            log.debug("coonection 关闭失败",e);
        }
    }

    @Override
    public boolean isClosed() {
        return isClosed==true;
    }

    /**
     * d
     */
    private void assertSessionNotClosed(){
        if(isClosed){
            throw new RuntimeException("该Session 已关闭");
        }
    }

    @Override
    public Long insert(String sql, boolean isOracle,Object... params) throws SQLException {
        assertSessionNotClosed();
        if(isOracle)
            return insetIntoOracle(sql,params);
       return insertIntoMysql(sql,params);
    }

    private Long insertIntoMysql(String sql,Object... params) throws SQLException {
        Long obj=null;
        PreparedStatement stas = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
        for(int i=1;i<params.length;i++){
            stas.setObject(i,params[i-1]);
        }
        stas.executeUpdate();//执行插入
        ResultSet set=stas.getGeneratedKeys();
        while(set.next()){
            obj=set.getLong(1);
        }
        return obj;
    }

    /**
     * 存储过程返回自动递增主键
     * @param sql
     * @param params
     * @return
     */
    private Long insetIntoOracle(String sql,Object... params) throws SQLException {
            CallableStatement call = conn.prepareCall(sql);
            for(int i=1;i<=params.length;i++){
                call.setObject(i,params[i-1]);
            }
            //存储过程注册输出变量
            call.registerOutParameter(params.length+1, OracleTypes.BIGINT);
            call.execute();
            Long obj=call.getLong(params.length+1);
            return obj;
    }
}
